﻿using System.Windows;
using System.Windows.Controls;
using System;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Windows.Input;

namespace SpeakupUI
{
    class BadDateSlot : Exception
    {
    }

    class BadTimeSlot : Exception
    { }

    class TimeSheet2: Grid 
    {
        public static DependencyProperty TimeSlotProperty;
        public static DependencyProperty DateSlotProperty;

        static TimeSheet2()
        {
            TimeSlotProperty = DependencyProperty.Register("TimeSlotDuration", typeof(TimeSlot), typeof(TimeSheet2));
            DateSlotProperty = DependencyProperty.Register("DateSlot", typeof (DateTime), typeof (TimeSheet2));
        }


        static readonly string DateFormat = "{0}\n{1,2:00}.{2,2:00}.{3,4}";

        List<TimeSlot> m_timeSlots = new List<TimeSlot>();
        public List<TimeSlot> TimeSlots
        {
            get
            {
                return m_timeSlots;
            }
        }

        List<DateTime> m_dateSlots= new List<DateTime>();
        public List<DateTime> DateSlots
        {
            get
            {
                return m_dateSlots;
            }
        }

        List<RowDefinition> m_gridBorderRows = new List<RowDefinition>();
        List<ColumnDefinition> m_gridBorderColumns = new List<ColumnDefinition>();
        List<Canvas> m_gridBorderCanvases = new List<Canvas>();

        Dictionary<UIElement, TimeSlot> m_childTimeSlots = new Dictionary<UIElement,TimeSlot> ();
        Dictionary<UIElement, DateTime> m_childDateSlots = new Dictionary<UIElement,DateTime> ();

        Brush m_highlighBrush = Brushes.Pink.Clone();        
        UIElement m_lastRowHighlight = null;
        UIElement m_lastColumnHighlight = null;

        Brush m_gridBorderBrush = Brushes.Black.Clone();
        double m_gridBorderSize = 1;
        double m_gridBorderMargin = 0;
        int m_gridBorderZIndex = -1;

        TimeSlot m_workingHours;
        public TimeSlot WorkingHours
        {
            get
            {
                return m_workingHours;
            }
        }

        public TimeSheet2(TimeSlot workingHours, TimeSpan slotDuration, uint days)
        {
            this.ShowGridLines = false;
            m_workingHours = workingHours;

            RowDefinition headerRow = new RowDefinition();
            headerRow.Height = GridLength.Auto;

            ColumnDefinition headerColumn = new ColumnDefinition();
            headerColumn.Width = GridLength.Auto;

            RowDefinitions.Add(headerRow);
            ColumnDefinitions.Add(headerColumn);

            Label rowHeaderLabel = new Label();
            rowHeaderLabel.VerticalAlignment = VerticalAlignment.Center;
            rowHeaderLabel.HorizontalAlignment = HorizontalAlignment.Center;

            Children.Add(rowHeaderLabel);
            Grid.SetRow(rowHeaderLabel, 0);
            Grid.SetColumn(rowHeaderLabel, 0);

            SetTimeSlots(workingHours, slotDuration);
            SetDateSlots(DateTime.Now, days);
            
            SetBorder();
        }

        void SetTimeSlots(TimeSlot workingHours, TimeSpan slotDuration)
        {
            m_timeSlots.Clear();

            long totalSlots = workingHours.Duration.Ticks / slotDuration.Ticks;

            for (int i = 0; i < totalSlots; i++)
            {
                TimeSlot slot = new TimeSlot();
                slot.Begin = new TimeSpan( workingHours.Begin.Ticks + slotDuration.Ticks * i ) ;
                slot.End = new TimeSpan(slot.Begin.Ticks + slotDuration.Ticks);
                m_timeSlots.Add(slot);

                RowDefinition rowdef = new RowDefinition();
                rowdef.Height = new GridLength(1, GridUnitType.Star);
                rowdef.DataContext = slot;
                RowDefinitions.Add(rowdef);

                Label lbl = new Label();

                lbl.VerticalAlignment = VerticalAlignment.Stretch;
                lbl.HorizontalAlignment = HorizontalAlignment.Stretch;
                
                lbl.Content = slot;
                lbl.Tag = slot;                
                Grid.SetRow(lbl, RowDefinitions.Count - 1);
                Grid.SetColumn(lbl, 0);
                Children.Add(lbl);
                
            }
        }
        void SetDateSlots(DateTime firstDate, uint days)
        {
            m_dateSlots.Clear();

            
            DateTime curDate = new DateTime(firstDate.Ticks);
            for (int i = 0; i < days; i++)
            {
                m_dateSlots.Add(curDate);

                ColumnDefinition coldef = new ColumnDefinition();
                coldef.Width = new GridLength(1, GridUnitType.Star);
                coldef.DataContext = curDate;
                ColumnDefinitions.Add(coldef);
                
                Label lbl = new Label();
                lbl.HorizontalAlignment = HorizontalAlignment.Center;
                lbl.VerticalAlignment = VerticalAlignment.Center;
                lbl.Content = String.Format(DateFormat, curDate.DayOfWeek, curDate.Day, curDate.Month, curDate.Year);

                Grid.SetColumn(lbl, i + 1);
                Grid.SetRow(lbl, 0);
                Children.Add(lbl);

                curDate = curDate + new TimeSpan(1, 0, 0, 0);
            }
        }
        void SetBorder()
        {
            int origColCount = ColumnDefinitions.Count;
            int origRowCount = RowDefinitions.Count;

            int colCount = origColCount * 2 - 1;
            int rowCount = origRowCount * 2 - 1;

            Canvas gridCanvas;

            foreach (UIElement child in Children)
            {
                int row = Grid.GetRow(child);
                Grid.SetRow(child, row * 2);

                int column = Grid.GetColumn(child);
                Grid.SetColumn(child, column * 2);
            }

            for (int i = 0; i < origRowCount - 1; i++)
            {
                gridCanvas = new Canvas();
                gridCanvas.Background = m_gridBorderBrush;
                gridCanvas.Margin = new Thickness(0, m_gridBorderMargin, 0, m_gridBorderMargin);
                
                m_gridBorderCanvases.Add(gridCanvas);
                Children.Add(gridCanvas);
                                
                RowDefinition rowdef = new RowDefinition();
                rowdef.Height = new GridLength(m_gridBorderSize, GridUnitType.Pixel);

                RowDefinitions.Insert(i * 2 + 1, rowdef);
                m_gridBorderRows.Add(rowdef);

                Grid.SetRow(gridCanvas, i * 2 + 1);
                Grid.SetColumn(gridCanvas, 0);
                Grid.SetColumnSpan(gridCanvas, colCount);
                Grid.SetZIndex(gridCanvas, m_gridBorderZIndex);
            }

            for (int i = 0; i < origColCount - 1; i++)
            {
                gridCanvas = new Canvas();
                gridCanvas.Background = m_gridBorderBrush;
                gridCanvas.Margin = new Thickness(m_gridBorderMargin, 0, m_gridBorderMargin,0);

                m_gridBorderCanvases.Add(gridCanvas);
                Children.Add(gridCanvas);

                ColumnDefinition coldef = new ColumnDefinition();
                coldef.Width = new GridLength(m_gridBorderSize, GridUnitType.Pixel);

                ColumnDefinitions.Insert(i * 2 + 1, coldef);
                m_gridBorderColumns.Add(coldef);

                Grid.SetColumn(gridCanvas, i * 2 + 1);
                Grid.SetRow(gridCanvas, 0);
                Grid.SetRowSpan(gridCanvas, rowCount);
                Grid.SetZIndex(gridCanvas, m_gridBorderZIndex);
            }
        }

        class GridLocation
        {
            public int column = 0;
            public int row = 0;
            public int rowSpan = 0;
        }
        GridLocation GetGridLocation(TimeSlot slot, DateTime date)
        {
            /* column index */

            int columnIndex = -1;
            foreach (DateTime dateSlot in m_dateSlots)
            {
                if (dateSlot.Date.Equals(date.Date))
                {
                    columnIndex = m_dateSlots.IndexOf(dateSlot);
                    break;
                }
            }

            if (columnIndex == -1)
            {
                throw new BadDateSlot();
            }
            columnIndex = (columnIndex + 1) * 2;

            /* row index */
            int rowBeginIndex = -1;
            foreach (TimeSlot timeSlot in m_timeSlots)
            {
                if (timeSlot.Begin <= slot.Begin &&
                    timeSlot.End > slot.Begin)
                {
                    rowBeginIndex = m_timeSlots.IndexOf(timeSlot);
                    break;
                }
            }

            if (rowBeginIndex == -1)
            {
                throw new BadTimeSlot();
            }
            rowBeginIndex = (rowBeginIndex + 1) * 2;


            int rowEndIndex = -1;
            foreach (TimeSlot timeSlot in m_timeSlots)
            {
                if (timeSlot.Begin < slot.End &&
                    timeSlot.End >= slot.End)
                {
                    rowEndIndex = m_timeSlots.IndexOf(timeSlot);
                    break;
                }
            }

            if (rowEndIndex == -1)
            {
                throw new BadTimeSlot();
            }
            rowEndIndex = (rowEndIndex + 1) * 2;

            GridLocation location = new GridLocation();
            location.column = columnIndex;
            location.row = rowBeginIndex;
            location.rowSpan = rowEndIndex - rowBeginIndex + 1;

            return location;
        }

        public void AddSlotChild(TimeSlot slot, DateTime date, UIElement child)
        {
            GridLocation location = GetGridLocation(slot, date);           

            Children.Add(child);
            Grid.SetColumn(child, location.column);
            Grid.SetRow(child, location.row);
            Grid.SetRowSpan(child, location.rowSpan);

            child.MouseEnter += ChildSlotMouseEnter;
            child.MouseLeave += ChildSlotMouseLeave;
            
            m_childTimeSlots.Add(child, slot);
            m_childDateSlots.Add(child, date);
        }

        public void RemoveSlotChild(UIElement child)
        {
            child.MouseEnter -= ChildSlotMouseEnter;
            child.MouseLeave -= ChildSlotMouseLeave;
            Children.Remove(child);
            m_childDateSlots.Remove(child);
            m_childTimeSlots.Remove(child);
        }

        void ChildSlotMouseEnter(object sender, MouseEventArgs e)
        {
            UIElement element = (UIElement)sender;
            GridLocation location = GetGridLocation(m_childTimeSlots[element], m_childDateSlots[element]);

            if (m_lastRowHighlight != null)
            {
                Children.Remove(m_lastRowHighlight);
            }

            /* highlight row */
            {
                Label label = new Label();
                label.Content = m_childTimeSlots[element];
                label.HorizontalAlignment = HorizontalAlignment.Stretch;
                label.VerticalAlignment = VerticalAlignment.Center;

                DockPanel panel = new DockPanel();

                panel.Children.Add(label);
                panel.Background = m_highlighBrush;

                m_lastRowHighlight = panel;
                Children.Add(panel);

                Grid.SetColumn(panel, 0);
                Grid.SetRow(panel, location.row);
                Grid.SetRowSpan(panel, location.rowSpan);
                Grid.SetZIndex(panel, 100);
            }

            /* highlight column */
            {
                Label lbl = new Label();
                lbl.HorizontalAlignment = HorizontalAlignment.Center;
                lbl.VerticalAlignment = VerticalAlignment.Center;

                DateTime curDate = m_childDateSlots[element];
                lbl.Content = String.Format(DateFormat, curDate.DayOfWeek, curDate.Day, curDate.Month, curDate.Year);
                
                DockPanel panel = new DockPanel();
                panel.Children.Add(lbl);
                panel.Background = m_highlighBrush;

                m_lastColumnHighlight = panel;
                Children.Add(panel);

                Grid.SetColumn(panel, location.column);
                Grid.SetRow(panel, 0);                
                Grid.SetZIndex(panel, 100);
            }


        }

        void ChildSlotMouseLeave(object sender, MouseEventArgs e)
        {
            if (m_lastRowHighlight != null)
            {
                Children.Remove(m_lastRowHighlight);
            }

            if (m_lastColumnHighlight != null)
            {
                Children.Remove(m_lastColumnHighlight);
            }
        }

    }
}
